/*
 * HalcyonV1.h
 *
 * Created: 01.05.2012 12:28:46
 *  Author: Maxim Karban <maxim.karban@gmail.com>
 */ 

#include "halcyon_v1.h"

#include "i2c_master.h"

#include <avr/io.h>
#include <math.h>
#include <util/delay.h>

uint8_t retry = 0;

void ERROR(void)
{
  for(int i = 0; i < 3; i++)
  {
    //error signal here
  }
}

void i2c_init(void)
{
  /* initialize TWI clock: XXX kHz clock, TWPS = 0 => prescaler = 1 */
  TWSR &= ~0x04;                         /* no prescaler */
  TWBR = ((F_CPU/SCL_CLOCK)-16)/2;  /* must be > 10 for stable operation */
  // Set pull up resistors
  DDRC  &= ~0x30;
  PORTC |=  0x30;
}// i2c_init



//0x08 A START condition has been transmitted
//0x10 A repeated START condition has been transmitted
void i2c_start(void)
{
  //send start
  TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);
  #ifndef SIMU
  retry = 0;
  while (!(TWCR & (1<<TWINT)))
    if(retry++ > 200)
      break;
  ;
  #endif
  //PORTC |=  0b00000010;

  if ((TWSR & 0xF8) != I2C_START)
  {
    ERROR();
  }
} //i2c_start

void i2c_restart(void)
{
  //send start
  TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);
  
  #ifndef SIMU
  retry = 0;
  while (!(TWCR & (1<<TWINT)))
    if(retry++ > 200)
      break;
  ;
  #endif
  
  //PORTC |=  0b00000010;

  if ((TWSR & 0xF8) != I2C_REPEATED_START)
  {
    ERROR();
  }
} //i2c_restart


void i2c_set_dev(unsigned char DevAddr)
{
  //set device address write/read
  TWDR = DevAddr;
  TWCR = (1<<TWINT) | (1<<TWEN);

  #ifndef SIMU
  retry = 0;
  while (!(TWCR & (1<<TWINT)))
    if(retry++ > 200)
      break;
  ;
  #endif
  
  if ((TWSR & 0xF8) != I2C_SLA_W_ACK)
  {
    ERROR();
  }
} //i2c_set_dev

void i2c_set_rdev(unsigned char DevAddr)
{
  //set device address write/read
  TWDR = DevAddr;
  TWCR = (1<<TWINT) | (1<<TWEN);

  #ifndef SIMU
  retry = 0;
  while (!(TWCR & (1<<TWINT)))
    if(retry++ > 200)
      break;
  ;
  #endif

  if ((TWSR & 0xF8) != I2C_SLA_R_ACK)
  {
    ERROR();
  }
}//i2c_set_rdev


void i2c_set_mem(unsigned char MemAddr)
{
  //set memory address

  TWDR = MemAddr;
  TWCR = (1<<TWINT) | (1<<TWEN);
  
  #ifndef SIMU
  retry = 0;
  while (!(TWCR & (1<<TWINT)))
    if(retry++ > 200)
      break;
  ;
  #endif
  
  if ((TWSR & 0xF8) != I2C_DATA_ACK)
  {
    ERROR();
  }

}//i2c_set_mem

void i2c_send_data(unsigned char Data)
{
  TWDR = Data;
  TWCR = (1<<TWINT) | (1<<TWEN);
  
  #ifndef SIMU
  retry = 0;
  while (!(TWCR & (1<<TWINT)))
    if(retry++ > 200)
      break;
  ;
  #endif

  if ((TWSR & 0xF8) != I2C_DATA_ACK)
  {
    ERROR();
  }

  TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTO);
}//i2c_send_data


void i2c_write(unsigned char DevAddr, unsigned char MemAddr, unsigned char Data)
{

  i2c_start();


  //set device address + write
  i2c_set_dev(DevAddr + I2C_WRITE);

  //set memory address
  i2c_set_mem(MemAddr);

  //send data byte
  i2c_send_data(Data);

  //PORTC &= ~0b00000010;

  //__delay_cycles(10);
  _delay_us(100);

} //i2c_write


void i2c_read(unsigned char *Buffer, unsigned char DevAddr, unsigned char MemAddr, unsigned char Cnt, unsigned char JustRead, unsigned char SetEnd)
{
  if(!JustRead){
    //send start
    i2c_start();

    //set device address + write
    i2c_set_dev(DevAddr + I2C_WRITE);

    //set memory address
    i2c_set_mem(MemAddr);

    //send start repeat
    i2c_restart();

    //set device address + read
    i2c_set_rdev(DevAddr + I2C_READ);

  }else{
  //  i2c_restart();
  }

  unsigned char ByteCount = Cnt;

  while(ByteCount>0)
  {
    //Get byte
    if(ByteCount>1){
      TWCR = (1<<TWINT) | (1<<TWEN) | (0<<TWSTO) | (1<<TWEA); //Next awaited
    }else{
      if(SetEnd){
      TWCR = (1<<TWINT) | (1<<TWEN) | (0<<TWSTO) | (0<<TWEA); //Last
      }else{
        TWCR = (1<<TWINT) | (1<<TWEN) | (0<<TWSTO) | (1<<TWEA); //Next awaited
      }
    }

    #ifndef SIMU
    retry = 0;
    while (!(TWCR & (1<<TWINT)))
      if(retry++ > 200)
        break;
    ;
    #endif
  
    //Diagnosis;
    switch(TWSR & 0xF8)
    {
      case 0x58: //NACK received
        if(ByteCount>1){
          ERROR();
        }
      break;
      case 0x50: //ACK  received
      break;
    }

    Buffer[Cnt - ByteCount] = TWDR;
    ByteCount--;
  }

  if((!JustRead)){
    if(SetEnd){
      TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTO);
    }

  }else{
    if(SetEnd){
      TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTO);
    }
  }

} //i2c_read
